07. Preference for Sort Order of Earthquakes in List

Preference for Sort Order of Earthquakes in List

Let's add one more user preference. Sometimes the user might want to know about the biggest earthquakes, but other times they might want to know about the most recent earthquakes instead. We can easily accommodate this using a ListPreference. First, let's define more strings in the string resource file.

In strings.xml:

 <!-- Strings For Order-By Preference [CHAR LIMIT=30] -->
 <string name="settings_order_by_label">Order By</string>
 <string name="settings_order_by_key" translatable="false">order_by</string>
 <string name="settings_order_by_default" translatable="false">@string/settings_order_by_magnitude_value</string>

 <!-- Label for order-by magnitude option [CHAR LIMIT=20] -->
 <string name="settings_order_by_magnitude_label">Magnitude</string>
 <string name="settings_order_by_magnitude_value" translatable="false">magnitude</string>

 <!-- Label for order-by most recent option [CHAR LIMIT=20] -->
 <string name="settings_order_by_most_recent_label">Most Recent</string>
 <string name="settings_order_by_most_recent_value" translatable="false">time</string>

Next, we'll package these strings up into string arrays by creating a new res/values/arrays.xml file.

In res/values/arrays.xml:

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string-array name="settings_order_by_labels">
         <item>@string/settings_order_by_magnitude_label</item>
         <item>@string/settings_order_by_most_recent_label</item>
     </string-array>

     <string-array name="settings_order_by_values">
         <item>@string/settings_order_by_magnitude_value</item>
         <item>@string/settings_order_by_most_recent_value</item>
     </string-array>
 </resources>

Then we can add a ListPreference to the res/xml/settings_main.xml file. The whole XML file should contain the following.

In res/xml/settings_main.xml:

 <?xml version="1.0" encoding="utf-8"?>
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:title="@string/settings_title">

     <ListPreference
         android:defaultValue="@string/settings_order_by_default"
         android:entries="@array/settings_order_by_labels"
         android:entryValues="@array/settings_order_by_values"
         android:key="@string/settings_order_by_key"
         android:title="@string/settings_order_by_label" />

     <EditTextPreference
         android:defaultValue="@string/settings_min_magnitude_default"
         android:inputType="numberDecimal"
         android:key="@string/settings_min_magnitude_key"
         android:selectAllOnFocus="true"
         android:title="@string/settings_min_magnitude_label" />

 </PreferenceScreen>

Then we need to look up the user’s preferred sort order when we build the URI for making the HTTP request. Read from SharedPreferences and check for the value associated with the key: getString(R.string.settings_order_by_key). When building the URI and appending query parameters, instead of hardcoding the “orderby” parameter to be “time”, we will use the user’s preference (stored in the orderBy variable).

In EarthquakeActivity.java:

     @Override
     public Loader<List<Earthquake>> onCreateLoader(int i, Bundle bundle) {

         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
         String minMagnitude = sharedPrefs.getString(
                 getString(R.string.settings_min_magnitude_key),
                 getString(R.string.settings_min_magnitude_default));

         String orderBy = sharedPrefs.getString(
                 getString(R.string.settings_order_by_key),
                 getString(R.string.settings_order_by_default)
         );

         Uri baseUri = Uri.parse(USGS_REQUEST_URL);
         Uri.Builder uriBuilder = baseUri.buildUpon();

         uriBuilder.appendQueryParameter("format", "geojson");
         uriBuilder.appendQueryParameter("limit", "10");
         uriBuilder.appendQueryParameter("minmag", minMagnitude);
         uriBuilder.appendQueryParameter("orderby", orderBy);

         return new EarthquakeLoader(this, uriBuilder.toString());
     }

Finally, we'll add additional logic in the EarthquakePreferenceFragment so that it is aware of the new ListPreference, similar to what we did for the EditTextPreference. In the onCreate() method of the fragment, find the “order by” Preference object according to its key. Then call the bindPreferenceSummaryToValue() helper method on this Preference object, which will set this fragment as the OnPreferenceChangeListener and update the summary so that it displays the current value stored in SharedPreferences.

In SettingsActivity.java, in EarthquakePreferenceFragment class:

 @Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     addPreferencesFromResource(R.xml.settings_main);

      Preference minMagnitude = findPreference(getString(R.string.settings_min_magnitude_key));
      bindPreferenceSummaryToValue(minMagnitude);

      Preference orderBy = findPreference(getString(R.string.settings_order_by_key));
      bindPreferenceSummaryToValue(orderBy);
 }

Since this is the first ListPreference that the EarthquakePreferenceFragment is encountering, update the onPreferenceChange() method in EarthquakePreferenceFragment to properly update the summary of a ListPreference (using the label, instead of the key).

In SettingsActivity.java, in EarthquakePreferenceFragment class:

 @Override
 public boolean onPreferenceChange(Preference preference, Object value) {
     String stringValue = value.toString();
     if (preference instanceof ListPreference) {
         ListPreference listPreference = (ListPreference) preference;
         int prefIndex = listPreference.findIndexOfValue(stringValue);
         if (prefIndex >= 0) {
             CharSequence[] labels = listPreference.getEntries();
             preference.setSummary(labels[prefIndex]);
         }
     } else {
         preference.setSummary(stringValue);
     }
     return true;
 }

And we're done with a simple settings activity! This is what the code should look like.

In SettingsActivity.java:

 package com.example.android.quakereport;

 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceManager;
 import android.support.v7.app.AppCompatActivity;

 public class SettingsActivity extends AppCompatActivity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.settings_activity);
     }

     public static class EarthquakePreferenceFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {

         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             addPreferencesFromResource(R.xml.settings_main);

             Preference minMagnitude = findPreference(getString(R.string.settings_min_magnitude_key));
             bindPreferenceSummaryToValue(minMagnitude);

             Preference orderBy = findPreference(getString(R.string.settings_order_by_key));
             bindPreferenceSummaryToValue(orderBy);
         }

         @Override
         public boolean onPreferenceChange(Preference preference, Object value) {
             String stringValue = value.toString();
             if (preference instanceof ListPreference) {
                 ListPreference listPreference = (ListPreference) preference;
                 int prefIndex = listPreference.findIndexOfValue(stringValue);
                 if (prefIndex >= 0) {
                     CharSequence[] labels = listPreference.getEntries();
                     preference.setSummary(labels[prefIndex]);
                 }
             } else {
                 preference.setSummary(stringValue);
             }
             return true;
         }

         private void bindPreferenceSummaryToValue(Preference preference) {
             preference.setOnPreferenceChangeListener(this);
             SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(preference.getContext());
             String preferenceString = preferences.getString(preference.getKey(), "");
             onPreferenceChange(preference, preferenceString);
         }
     }
 }

When you run the app, go to settings, and modify the sort order based on magnitude or time, and see how the list of earthquakes changes!
Take a look at this repo to see the changes: https://github.com/udacity/ud843-QuakeReport/commit/05190298d7e6a7de21e3beb8f940e133fea67b4c